package model

import (
	"bufio"
	"bytes"
	j_log "config-client-v1/init/j-log"
	"config-client-v1/utils/tools"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/olekukonko/tablewriter"
	"io"
	"net"
	"os"
	"runtime/debug"
	"strings"
	"time"
)

type CAN20BConfig struct {
	Mode            string `json:"mode"`             // TCP模式, [client,server]
	IP              string `json:"ip"`               // IP
	Port            int    `json:"port"`             // 端口
	ConnectTimeout  int    `json:"connect_timeout"`  // 连接超时时间 (单位ms)
	ResponseTimeout int    `json:"response_timeout"` // 响应超时时间 (单位ms)
}

func CreateCanFrame(frame *Frame, dataPart []byte) (data []byte, err error) {
	if len(dataPart) < 8 {
		padRight := bytes.Repeat([]byte{0}, 8-len(dataPart))
		dataPart = append(dataPart, padRight...)
	}
	frameAddrBytes, err := tools.HexStrToByteArray(frame.FrameAddr)
	if err != nil {
		err = errors.New(fmt.Sprintf("can_run.CreateCanFrame frame.FrameAddr 转换为字节数组失败: %s", err.Error()))
		return
	}
	headBits := ""
	if frame.FrameMode == "std" {
		headBits += "0" //标准帧 0
	} else {
		headBits += "1" // 扩展帧 1
	}
	if frame.FrameFormat == "data" {
		headBits += "0" // 数据帧 0
	} else {
		headBits += "1" // 远程帧 1
	}
	headBits += "00" // 保留值 00
	if frame.Length > 8 {
		err = errors.New(fmt.Sprintf("CAN帧内容长度不可超过8字节"))
		return
	}
	binStr := tools.ByteToBinaryString(byte(frame.Length))
	lowerStr := binStr[4:8]
	headBits += lowerStr
	fmt.Printf("headBits = %s\n", headBits)
	headByte, err := tools.BinaryStringToByte(headBits)
	if err != nil {
		err = errors.New(fmt.Sprintf("CAN帧headBits转换失败: %s, headBits=%s", err.Error(), headBits))
		return
	}
	data = append(data, headByte)
	data = append(data, frameAddrBytes...)
	data = append(data, dataPart...)
	return
}

func (ppe *PPE) CANTCPClientFrameSendProcess(ctx context.Context, frame *Frame) {
	defer func() {
		if err := recover(); err != nil {
			j_log.LogError([]interface{}{"CANTCPClientFrameSendProcess panic", err})
			fmt.Println(string(debug.Stack()))
		}
		j_log.LogInfo([]interface{}{fmt.Sprintf("frame_id=%d, CANTCPClientFrameSendProcess 协程已关闭", frame.ID)})
	}()
	for {
		select {
		case <-ctx.Done():
			return
		default:
			if frame.SendType == 1 {
				list := make([]*FrameItemParam, 0)
				err := json.Unmarshal([]byte(frame.JsonDetail), &list)
				if err != nil {
					j_log.LogError([]interface{}{fmt.Sprintf("frame_id=%d, 发送帧json解析失败 frame.JsonDetail; %s", frame.ID, err.Error())})
					return
				}
				data := make([]byte, 0)
				if frame.DataMode == 1 {
					data, err = CreateFrameRawData(list)
					if err != nil {
						j_log.LogError([]interface{}{fmt.Sprintf("frame_id=%d, 发送帧生成数据部分失败; %s", frame.ID, err.Error())})
						return
					}
				} else {
					data, err = tools.HexStrToByteArray(frame.DataContent)
					if err != nil {
						j_log.LogError([]interface{}{fmt.Sprintf("frame_id=%d, 发送帧生成数据部分失败; %s", frame.ID, err.Error())})
						return
					}
				}
				fullData, err := CreateCanFrame(frame, data)
				if err != nil {
					j_log.LogError([]interface{}{fmt.Sprintf("frame_id=%d, 发送帧生成CAN帧失败; %s", frame.ID, err.Error())})
					return
				}
				j_log.LogInfo([]interface{}{fmt.Sprintf("【send】%s, ppe_id=%d len=%d %s", ppe.ProtocolName, ppe.ID, len(fullData), tools.ByteArrayToHexStr(fullData))})
				_, err = ppe.TcpClient.Connection.Write(fullData)
				if err != nil {
					j_log.LogError([]interface{}{fmt.Sprintf("ppe_id=%d  tcp发送失败:%s", ppe.ID, err.Error())})
					time.Sleep(time.Duration(frame.ScanRate) * time.Millisecond)
					continue
				}
				//j_log.LogInfo([]interface{}{fmt.Sprintf("【send】%s:ppe_id=%d, frame_id=%d, data = %v", ppe.ProtocolName, ppe.ID, frame.ID, data)})
				time.Sleep(time.Duration(frame.ScanRate) * time.Millisecond)
			} else if frame.SendType == 2 { // 事件触发
				match, err := IsTriggerConditionMatched(frame)
				if err != nil {
					j_log.LogError([]interface{}{fmt.Sprintf("[%s] frame_id=%d, IsTriggerConditionMatched error %s", frame.Name, frame.ID, err.Error())})
					return
				}
				if match {
					j_log.LogInfo([]interface{}{fmt.Sprintf("[%s] frame_id=%d, 事件触发帧满足条件", frame.Name, frame.ID)})
					if time.Now().Sub(frame.LastTriggerTime).Seconds()*1000 < float64(frame.TriggerInterval) {
						j_log.LogInfo([]interface{}{fmt.Sprintf("[%s] frame_id=%d, 事件触发帧距离下一次触发时间间隔未到, 跳过", frame.Name, frame.ID)})
						time.Sleep(1000 * time.Millisecond)
						continue
					}

					list := make([]*FrameItemParam, 0)
					err := json.Unmarshal([]byte(frame.JsonDetail), &list)
					if err != nil {
						j_log.LogError([]interface{}{fmt.Sprintf("frame_id=%d, 发送帧json解析失败 frame.JsonDetail; %s", frame.ID, err.Error())})
						return
					}
					data := make([]byte, 0)
					if frame.DataMode == 1 {
						data, err = CreateFrameRawData(list)
						if err != nil {
							j_log.LogError([]interface{}{fmt.Sprintf("frame_id=%d, 发送帧生成数据部分失败; %s", frame.ID, err.Error())})
							return
						}
					} else {
						data, err = tools.HexStrToByteArray(frame.DataContent)
						if err != nil {
							j_log.LogError([]interface{}{fmt.Sprintf("frame_id=%d, 发送帧生成数据部分失败; %s", frame.ID, err.Error())})
							return
						}
					}
					fullData, err := CreateCanFrame(frame, data)
					if err != nil {
						j_log.LogError([]interface{}{fmt.Sprintf("frame_id=%d, 发送帧生成CAN帧失败; %s", frame.ID, err.Error())})
						return
					}
					j_log.LogInfo([]interface{}{fmt.Sprintf("【send】%s, ppe_id=%d len=%d %s", ppe.ProtocolName, ppe.ID, len(fullData), tools.ByteArrayToHexStr(fullData))})
					_, err = ppe.TcpClient.Connection.Write(fullData)
					if err != nil {
						j_log.LogError([]interface{}{fmt.Sprintf("ppe_id=%d  tcp发送失败:%s", ppe.ID, err.Error())})
						time.Sleep(1000 * time.Millisecond)
						continue
					}

					frame.LastTriggerTime = time.Now()
				} else {
					j_log.LogInfo([]interface{}{fmt.Sprintf("[%s] frame_id=%d, 事件触发帧不满足条件, 跳过", frame.Name, frame.ID)})
				}
				time.Sleep(1000 * time.Millisecond)
			}
		}
	}
}

func (ppe *PPE) RunCANTCPClient() {
	defer func() {
		if err2 := recover(); err2 != nil {
			j_log.LogError([]interface{}{"RunCANTCPClient panic", err2})
			fmt.Println(string(debug.Stack()))
		}

		if ppe.RunningState == 3 {
			j_log.LogInfo([]interface{}{"【已停止】", ppe.ProtocolName, ppe.Name})
			ppe.RunningState = 4
			ppe.UnRegister()
			_ = ppe.UpdateRunningState(4, "设备已停止")
			return
		}

		ppe.RunningState = 5
		j_log.LogInfo([]interface{}{"3秒后重连...", ppe.Name})
		ppe.Detail = "正在尝试重连..."
		_ = ppe.UpdateRunningState(5, "正在尝试重连...")
		time.Sleep(3 * time.Second)
		/*--- 这段代码写两次, 这里确保在3秒sleep内的停止操作生效 ---*/
		if ppe.RunningState == 3 {
			j_log.LogInfo([]interface{}{"【已停止】", ppe.ProtocolName, ppe.Name})
			ppe.RunningState = 4
			ppe.UnRegister()
			_ = ppe.UpdateRunningState(4, "设备已停止")
			return
		}

		ppe.RunCANTCPClient()
	}()
	ppe.Register()
	if ppe.TcpClient == nil {
		ppe.TcpClient = &TCPClient{
			Requests:      NewFSTCPRequestMap(),
			FrameCacheMap: NewFrameCacheMap(),
		}
	}
	config := CAN20BConfig{}
	err0 := json.Unmarshal([]byte(ppe.Config), &config)
	if err0 != nil {
		ppe.RunningState = 3
		ppe.Detail = fmt.Sprintf("CAN20BConfig json解析失败: %v", err0.Error())
		return
	}

	addr := fmt.Sprintf("%s:%d", config.IP, config.Port)
	//j_log.LogInfo([]interface{}{"正在连接server", ppe.Name, config.IP, config.Port})
	conn, err0 := net.Dial("tcp", addr)
	if err0 != nil {
		ppe.tcpErrorHandle(err0)
		return
	}
	defer conn.Close()

	j_log.LogInfo([]interface{}{fmt.Sprintf("【已启动】%s:%s, %s:%d", ppe.ProtocolName, ppe.Name, config.IP, config.Port)})
	ppe.TcpClient.Connection = conn
	ppe.TcpClient.RemoteAddr = conn.RemoteAddr().String()
	ppe.TcpClient.Reader = bufio.NewReader(ppe.TcpClient.Connection) // 创建一个tcp返回 reader

	ppe.RunningState = 2
	ppe.Detail = "已启动"
	_ = ppe.UpdateRunningState(2, "已启动")
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// 异步处理发送帧
	for _, frame := range ppe.FrameList {
		if frame.FrameType == "send" && frame.Status == 1 {
			if frame.SendType >= 1 && frame.SendType <= 2 {
				go ppe.CANTCPClientFrameSendProcess(ctx, frame)
			}
		}
	}

	var cacheBuffer [ByteLENGTH]byte
	// 同步处理接收帧
	for {
		n, err := ppe.TcpClient.Reader.Read(cacheBuffer[0:])
		if err != nil {
			if err == io.EOF {
				ppe.Detail = "read error io.EOF." + err.Error()
				j_log.LogError([]interface{}{"read error io.EOF.", ppe.Name, config.IP, config.Port})
			} else if err == net.ErrClosed || strings.Contains(err.Error(), "use of closed network connection") || strings.Contains(err.Error(), "read: connection reset by peer") || strings.Contains(err.Error(), "closed by the remote host") {
				ppe.Detail = "read error PING." + err.Error()
				j_log.LogError([]interface{}{"read error PING.", ppe.Name, config.IP, config.Port})
			} else {
				ppe.Detail = "read error unknown." + err.Error()
				j_log.LogError([]interface{}{"read error unknown.", ppe.Name, config.IP, config.Port, err.Error()})
			}
			return
		}
		if n < 13 {
			j_log.LogError([]interface{}{"数据长度太小, 跳过此帧", n})
			continue
		}
		if n%13 != 0 {
			j_log.LogError([]interface{}{"数据长度不是13的倍数, 跳过此帧", n})
			continue
		}
		now := tools.GetNow()
		for i := 0; i < n; i += 13 {
			buffer := cacheBuffer[i : i+13]
			str0 := tools.ByteArrayToHexStr(buffer[:])
			j_log.LogInfo([]interface{}{fmt.Sprintf("【recv】%s, ppe_id=%d len=%d %s", ppe.ProtocolName, ppe.ID, 13, str0)})
			_ = tools.WriteStringToLog(fmt.Sprintf("[%s~%s]【recv】len=%d#%s", tools.GetNow(), ppe.TcpClient.RemoteAddr, n, str0), "can_tcp_data_all.log", "a") // 可能是粘包异常
			// 输出读到的数据
			//time.Sleep(5 * time.Millisecond) 88 08824100 61B0B73FA3282332
			//continue
			frameAddr := tools.ByteArrayToHexStr(buffer[1:5])
			buffer = buffer[5:]
			frameMatched := false
		loop1:
			for _, frame := range ppe.FrameList {
				if !(frame.FrameType == "recv" && frame.Status == 1) {
					continue
				}
				if frame.FrameAddr != frameAddr {
					continue
				}
				/*--- 变量解析开始 ---*/
				itemList := make([]*FrameItemParam, 0)
				err = json.Unmarshal([]byte(frame.JsonDetail), &itemList)
				if err != nil {
					j_log.LogError([]interface{}{fmt.Sprintf("%s json解析失败 frame.JsonDetail; %s", ppe.Name, err.Error())})
					return
				}

				data := make([][]string, 0)
				messageList := make([]*QueueValue, 0)
				for _, item := range itemList {
					var value float64
					switch item.SpecType {
					case "bool":
						//fmt.Println("单字节:", value, item.MultiByte, item.BoolBits)
						if item.MultiByte == 1 {
							binArr := tools.ByteToBitArray(buffer[item.Start])
							value = float64(binArr[item.BoolBits])
						} else {
							value = float64(buffer[item.Start])
						}
					case "uint8":
						v := buffer[item.Start]
						value = float64(v)
					case "int8":
						v := buffer[item.Start]
						value = float64(int8(v))
					case "uint16":
						rawBytes := tools.BytesPaddingLeft(buffer[item.Start:item.End+1], 2)
						v := tools.BytesToUint16(rawBytes, item.Endian)
						value = float64(v)
					case "int16":
						rawBytes := tools.BytesPaddingLeft(buffer[item.Start:item.End+1], 2)
						v := tools.BytesToUint16(rawBytes, item.Endian)
						value = float64(int16(v))
					case "uint32":
						rawBytes := tools.BytesPaddingLeft(buffer[item.Start:item.End+1], 4)
						v := tools.BytesToUint32(rawBytes, item.Endian)
						value = float64(v)
					case "int32":
						rawBytes := tools.BytesPaddingLeft(buffer[item.Start:item.End+1], 4)
						v := tools.BytesToUint32(rawBytes, item.Endian)
						value = float64(int32(v))
					case "uint64":
						rawBytes := tools.BytesPaddingLeft(buffer[item.Start:item.End+1], 8)
						v := tools.BytesToUint64(rawBytes, item.Endian)
						value = float64(v)
					case "int64":
						rawBytes := tools.BytesPaddingLeft(buffer[item.Start:item.End+1], 8)
						v := tools.BytesToUint64(rawBytes, item.Endian)
						value = float64(int64(v))
					case "float32":
						v := tools.BytesToFloat32(buffer[item.Start:item.End+1], item.Endian)
						value = float64(v)
					case "float64":
						v := tools.BytesToFloat64(buffer[item.Start:item.End+1], item.Endian)
						value = v
					}
					data = append(data, []string{
						item.Addr,
						item.FieldName,
						item.SpecType,
						tools.ByteArrayToHexStr(buffer[item.Start : item.End+1]),
						fmt.Sprintf("%v", tools.FormatFloat(value)),
						fmt.Sprintf("%v", item.MultiByte),
						fmt.Sprintf("%v", item.Start),
						fmt.Sprintf("%v", item.End),
						fmt.Sprintf("%v", item.BoolBits),
					})

					// 变量保存到 redis 队列
					queueData := QueueValue{}
					queueData.SpecType = item.SpecType
					queueData.Addr = item.Addr
					queueData.FieldName = item.FieldName
					queueData.Value = value
					queueData.CreateTime = &now
					messageList = append(messageList, &queueData)
				}

				if len(messageList) > 0 {
					go VariablesMap.PushMessageToClients(messageList)
				}
				/*--- 变量解析结束 ---*/
				if len(data) > 0 {
					table := tablewriter.NewWriter(os.Stdout)
					table.SetHeader([]string{"地址", "变量名", "类型", "原始数据", "解析值", "是否多字节", "START", "END", "BIT"})
					for _, v := range data {
						table.Append(v)
					}
					table.Render()
				}
				frameMatched = true // 匹配到合适帧格式, 终止循环
				if frame.RelatedFrameID != 0 {
					go func(relatedFrameID int) {
						err = RelationFrameTrigger(relatedFrameID)
						if err != nil {
							j_log.LogError([]interface{}{fmt.Sprintf("%s 触发关联帧失败; %s", ppe.Name, err.Error())})
						}
					}(frame.RelatedFrameID)
				}
				break loop1
			}
			if frameMatched == false {
				j_log.LogInfo([]interface{}{fmt.Sprintf("??? 未知的数据包, frame_addr=%s", frameAddr)})
			}
		}
	}
}
